跳到主要内容

Redis Cluster 迈向分布式

Redis Cluster 是什么

Redis Cluster 是 Redis 分布式集群的实现,它是一个高可用性的分布式数据库解决方案。Redis Cluster 可以自动将数据分散到多个 Redis 节点上,提高数据的可用性和容错性。

在 Redis Cluster 中,所有节点都是对等的,每个节点都可以处理读写请求。同时,Redis Cluster 还提供了数据的复制和故障转移机制,可以保证数据的可靠性和高可用性。

Redis Cluster 将数据分散到多个节点上,每个节点负责一部分数据。这些节点之间通过 Gossip 协议来进行通信和数据同步。当有节点故障时,Redis Cluster 会自动将故障节点的数据迁移到其他节点上,保证数据的可用性。

总体来说,Redis Cluster 是一个功能强大的分布式数据库解决方案,适用于需要高可用性、高性能、分布式存储和处理的场景。

Redis Cluster 怎么解决并发的?

Redis Cluster 使用了分片(sharding)的方式来实现数据的水平扩展和并发写入的支持。它将整个数据集划分为多个分片,并将每个分片分布在不同的节点上,通过分布式存储和处理来支持并发写入。

下面是 Redis Cluster 如何保证并发写入的工作原理:

  1. 数据分片:Redis Cluster 将整个数据集分为多个分片,每个分片由多个主节点(master)和零个或多个从节点(slave)组成。每个分片负责存储和处理一部分数据。

  2. 主从复制:在每个分片中,有一个主节点负责处理写入请求,并将数据复制到对应的从节点上。从节点通过复制主节点的数据来保持数据的一致性。这样可以提高系统的可用性和容错性。

  3. 数据迁移和重新分片:Redis Cluster 具备动态的数据迁移和重新分片功能。当集群需要进行扩容或缩容时,它可以自动将数据从一个节点迁移到另一个节点,并重新分配分片的负载,以平衡数据的存储和处理负载。这样可以保证并发写入请求能够均匀地分布在各个节点上,减少单个节点的负载压力。

  4. 主节点选择:Redis Cluster 使用哈希槽(hash slot)来划分数据,并将每个哈希槽分配给一个主节点。客户端在进行写入操作时,首先根据数据的键计算出哈希槽,然后将写入请求发送到对应的主节点。这样可以确保相同键的写入操作都落在同一个主节点上,避免对同一数据的并发写入冲突。

  5. 冲突解决:当多个客户端同时对同一个键进行写入操作时,可能会出现冲突。Redis Cluster 使用基于 Raft 的内部协议来解决写入冲突,并确保数据的一致性。通过多数派投票的方式,协议能够选举出一个领导者(leader),并在领导者上执行写入操作。其他节点(follower)会复制领导者的写入操作,以保持数据的一致性。

通过上述的机制和策略,Redis Cluster 实现了数据的水平扩展和并发写入的支持。它能够自动进行数据迁移和重新分片,动态调整负载,保证数据的一致性,并通过主从复制和内部协议来保障数据的可用性和容错性。

比如:100w 条数据,5 个 master,每个 master 就负责存储 20w 条数据,分布式数据存储

Redis Cluster 的工作原理

Redis Cluster 将数据划分为多个片段,每个片段由一个或多个节点负责存储和处理,从而实现数据的分布式存储。

在 Redis Cluster 中,各个节点之间通过 Gossip 协议来进行通信,通过互相交换信息来发现新的节点、检测节点状态、同步数据等。

20230509142143

Redis Cluster 的两个 TCP 端口

在 Redis Cluster 中,每个节点需要打开两个 TCP 端口,分别是普通的 Redis 端口和集群总线端口(cluster bus port),这个端口默认是加 1w 的端口号 16379。

普通的 Redis 端口用于客户端连接和发送 Redis 命令,这个端口在 Redis 单节点中也需要使用。

集群总线端口(cluster bus port)是用于节点之间的点对点通信的 TCP 端口,主要用于数据同步、执行故障转移等操作。这些操作需要使用可靠的协议,例如 TCP 协议,以确保数据的可靠性和一致性。因此,Redis Cluster 使用了 TCP 端口来实现这些操作。

主从怎么做的数据一致?

在 Redis 主从架构中,主节点负责处理写入操作,而从节点则通过复制(Replication)机制从主节点获取数据并进行同步,以达到数据一致性的目的。下面是主从数据一致性的工作流程:

1、快照同步(Snapshot Sync):当从节点刚刚连接到主节点时,主节点会将当前数据集的快照发送给从节点。从节点接收到快照后,会将其加载到内存中,使从节点的数据与主节点的数据一致。这是一种初始同步的方式,用于确保从节点和主节点的数据集基本相同。

2、命令传播(Command Propagation):在初始同步后,主节点会将写入操作(命令)记录在内存中的 AOF(Append-Only File)日志中,并向连接的从节点发送这些写入命令。从节点接收到命令后,会按照相同的顺序执行这些命令,以实现与主节点的数据一致性。

提示

注意,从节点执行的是相同的命令序列,而不是直接复制主节点的数据。因此,从节点会通过执行相同的命令来达到与主节点数据的同步。

3、增量同步(Incremental Sync):在初始同步和命令传播后,主节点会将写入操作(命令)以增量的方式发送给从节点。增量同步是通过在主节点的 AOF 文件中记录写入操作并传输给从节点来实现的。从节点接收到增量命令后,会按照顺序执行这些命令以保持与主节点的数据一致。

通过这些机制,Redis 主从架构实现了数据的一致性。主节点负责接收和处理写入操作,从节点通过复制机制从主节点获取写入命令并执行,以保持与主节点的数据一致。

备注

虽然 Redis 中的命令传播(Command Propagation)机制可以将写入操作(命令)从主节点传播到从节点,但为什么还需要增量同步(Incremental Sync)呢?这是因为命令传播和增量同步在主从复制中发挥着不同的作用:

1、命令传播:命令传播是在主节点执行写入操作后,将相应的写入命令发送给连接的从节点,以保持主从数据的一致性。通过命令传播,从节点可以按照相同的顺序执行写入命令,以确保与主节点的数据一致。

命令传播的优点是实时性好,从节点可以尽快地获取并执行主节点上的写入操作,以保持数据的同步。它能够快速将主节点的写入操作传播给从节点,使得从节点能够追赶上主节点的数据更新。

然而,命令传播的方式只能处理新的写入操作,不能处理历史数据的同步。如果从节点在主节点故障期间断开连接,或者从节点加入主节点时数据已经发生了变化,那么仅通过命令传播无法完全保证数据的一致性。

2、增量同步:增量同步是通过主节点的 AOF(Append-Only File)日志来实现的。主节点将写入操作以增量的方式记录在 AOF 文件中,并将增量数据发送给连接的从节点。从节点接收到增量数据后,按照顺序执行这些增量操作,从而保持与主节点的数据一致。

增量同步的作用是在主节点和从节点之间进行数据的补偿和同步。即使从节点在主节点故障期间或连接断开后重新连接,通过增量同步可以获取从节点在断开期间缺失的写入操作,并将数据与主节点进行同步。

增量同步的过程可能会比命令传播慢一些,因为它需要从 AOF 日志中解析出增量操作,并将其传输给从节点。但它可以确保从节点能够获取到历史数据的更新,并与主节点保持一致。

因此,命令传播和增量同步在 Redis 主从复制中起到互补的作用。命令传播用于实时传播新的写入操作,使从节点尽快获得更新的数据。而增量同步用于在断开连接或故障转移等情况下补偿数据,保证数据的完整性和一致性。两者结合使用可以实现较高的数据可靠性和一致性。

需要注意的是,Redis 主从复制是异步的,从节点与主节点之间存在一定的延迟。因此,从节点的数据可能会稍有滞后于主节点,但这不影响整体的数据一致性。在实际应用中,可以根据需求选择合适的配置和监控机制来确保主从节点的数据一致性和可用性。

某个主节点挂了会怎么样?

当 Redis 多主多从架构中的某个主节点挂掉时,会出现以下情况:

  1. 主节点故障:如果其中一个主节点发生故障,将无法继续接收写入操作。客户端在尝试向该主节点写入数据时,可能会收到连接失败或超时的错误。这时,主节点不再对外提供写入服务。

  2. 自动故障转移:Redis Sentinel 或 Redis Cluster 等管理工具可以监测主节点的健康状态,并自动进行故障转移。当主节点挂掉时,管理工具会自动将一个从节点提升为新的主节点,接管原主节点的写入操作,并继续提供服务。这个过程通常是自动化的,无需手动干预。

  3. 从节点的角色变化:当主节点挂掉后,原来的从节点会自动转变为新的主节点。新的主节点将不再复制其他节点的数据,而是接收客户端的写入操作,并将写入的数据复制给其他从节点。

  4. 数据同步延迟:当发生主节点故障并进行自动故障转移时,新的主节点可能会与原主节点有一定的数据同步延迟。这是由于复制机制的异步特性和网络延迟等因素造成的。从节点需要一定时间来从新的主节点获取增量命令并同步数据,期间数据可能会有所滞后。

需要注意的是,自动故障转移并不是实时的,需要一定时间来完成。在这段时间内,系统的可用性可能会有所下降。因此,当主节点挂掉时,应该及时检查并确保故障转移的进行,并在适当的时候进行故障恢复。

另外,为了保证高可用性和数据一致性,可以配置多个主节点和多个从节点,并将读操作分布到不同的从节点上,以实现负载均衡和容错能力。这样即使某个主节点挂掉,其他主节点和从节点仍然可以继续提供服务,并保持数据一致性。

选择主库的过程

从库挂了也没事,客户端还能接着向其他库发送请求,那要是主库挂了咋办?大哥都没了,都没人给我们送钱了(同步数据),那还服务个屁呀,不淦了不淦了!!!

开玩笑开玩笑,为人民服务,大哥没了我们再选一个不就完事了吗?不想当大哥的小弟不是好小弟,嘿嘿嘿,这个时候我们另外一个重要的角色就登场了------哨兵

如果都是读请求,那好像没啥问题,万一要写了呢?而且肯定会写呀,主从模式是读写分离的,需要大哥也就是主库写数据的,我们看个图捋捋思路:

上面说了个解决方法,那就是让小弟们再选一个大哥出来,也就是,重新选一个主库出来。但是有几个小问题:

1、你确定大哥是真的挂了?不是睡着了?

2、还有选那个小弟呢?

3、怎么吧新上任的大哥的信息通知给客户端呢?

带着这些问题我们再往下分析。

这就不得不说哨兵机制了。在主从集群中,哨兵机制就是实现从库字段切换的关键机制,它负责解决上面的三个问题。

哨兵机制的基本流程

首先它是个进程,主从库实例运行的时候他也在运行,他的职责就是负责监控、选主库、通知客户端。

先说说监控:就是检查看哪个实例不正常,如果从库实例没响应,哨兵就给他个标记,这个小弟不在了;如果主库没有响应,也给个标记,大哥也下线了,然后这个时候就开始自动切换主库。

具体怎么检查呢?很简单,就是在哨兵运行期间,周期的 ping 各个实例,看是否有响应。

切换主库流程就是哨兵的第二个任务选主库,按照一定的规则选出来。具体什么规则我们一会儿再说,别着急。

然后就是通知了,哨兵会把新主库的连接信息发送给其他从库,让他们执行 replicaof 命令,和新主库进行连接,并且同步数据。同样也会把新主库的连接信息发送给客户端,让他们把请求操作发到新主库上。

再来个图:

三个任务中,通知是最简单的,也不需要做什么决策,直接通知就行了。

难得是监控和选主这两方面!!!为啥呢?凭啥你说我下线就下线了?我睡着了还不行?还有你凭啥就把二狗子选上了?三愣子他得罪你了吗?

看看,哨兵也不容易呀!

不过总有解决方式的,我们先说说下线这个问题。

哨兵对主库的下线还有两个判断,主观下线和客观下线两种

主观下线和客观下线

主观下线就是哨兵用 PING 命令,如果实例响应超时,这就判断主观下线。

检查的如果是从库,标记好久 ok 了,影响不大,下线对外的服务也不会间断,如果是主库,就不能直接标记下线,因为有可能哨兵误判!!!,这影响就大了。

啥叫误判?就是主库实际并没有下线,只是可能集群网络压力大,拥堵,或者主库本身压力大导致没有及时响应 PING 命令。

那咋办嘛?

那我多搞几个哨兵不就行了?总不能每个都误判吧!!!这就是哨兵集群

只有大多数的哨兵判断主库主观下线,然后主库才会被标记客观下线,这里有个原则,少数服从多数。这也会进一步触发哨兵开始主从切换流程。

到这里我们就知道如何确定主库是否下线了

确定主库确实是完蛋咯,那我们还的接着服务呀,这个时候就要选一个新的主库了。

如何选定主库

哨兵选这个过程简单可以说成是 筛选 + 打分

筛选就是按照一定的条件把不符合的筛出去,就比如说二狗子天天拔你网线(一个从库实例老是连接中断。成了主库还是断,那选他搞毛啊),你肯定要把他给筛出去呀,然后再按照一定的规则给剩下的从库打分,谁的分高就选谁!!!

再来个图:

筛选条件具体咋判断呢?

Redis 有个配置项 down-after-milliseconds * 10,这个是我们认定主从库断连的最大连接超时时间,如果这个时间内都没有连接成功,我们就认定主从节点断连了,如果断连次数超过了10次,说明这个从库的网络状态不好,不适合上位当主库。

筛选完了,开始打分了。

按照三个规则打三轮分,这三个规则分别是从库优先级,从库复制进度以及从库ID号

第一轮从库优先级:

用户通过 slave-prioity 配置项,设置从库的优先级。这个是手动设置的。

如果优先级都一样那就进行第二轮被呗!!!

第二轮和旧主库同步程度最接近的从库得分高

如何判断呢?

之前介绍过主从同步时,主库会用 master_repl_offset 记录当前的最新写操作在 repl_backlog_buffer 的位置,从库的 slave_repl_offset 最接近这个位置就得分高。

再来个图:

当然如果位置接近的值都一样,那就进行第三轮呗!!1

第三轮:ID号最小的从库得分高

在选择主库时有个默认的规则:优先级和复制进度相同的情况下,ID 号最小的从库得分高。这个ID 值是每个实例都不同的,类似与编号。

这里就基本完成了上位选主库的操作。

References